//
//  Base32.m
//  App
//
//  Created by gome on 2018/11/29.
//  Copyright © 2018 gome. All rights reserved.
//

#import "Base32.h"

static const char *defaultBaseList = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";

@implementation Base32

/**
 base32 加密
 
 @param originString 被加密字符串
 @param keyList 加密key
 @return 加密后的字符串
 */
+ (NSString *)enCodeBase32String:(NSString *)originString keyList:(NSString *)keyList {
    if (!originString) {
        return nil;
    }
    
    const char *myKeyList = defaultBaseList;
    if (keyList) {
        NSString *keyString = keyList;
        myKeyList = [keyString UTF8String];
    }
    
    const char *bytes = [originString UTF8String];
    long originSize = strlen(bytes);
    
    long baseSize = 2 * strlen(bytes) + 1;
    char base16Char[baseSize];
    
    encodeBase32(bytes, originSize, myKeyList, base16Char);
    NSString *base16String = [NSString stringWithCString:base16Char encoding:NSASCIIStringEncoding];
    
    return base16String;
}





/**
 base32 解密
 
 @param base16String 解密字符串
 @param keyList 解密key
 @return 解密后的字符串
 */
+ (NSString *)deCodeBase32String:(NSString *)base16String keyList:(NSString *)keyList {
    if (!base16String) {
        return nil;
    }
    
    const char *myKeyList = defaultBaseList;
    if (keyList) {
        NSString *keyString = keyList;
        myKeyList = [keyString UTF8String];
    }
    
    const char *base16Char = [base16String UTF8String];
    
    long base32Size = strlen(base16Char);
    char originChar[base32Size];
    
    decodeBase32(base16Char, base32Size, myKeyList, originChar);
    NSString *originString = [NSString stringWithCString:originChar encoding:NSASCIIStringEncoding];
    
    return originString;
    
}



static char *encodeBase32(const char buff[], const long size, const char *baseCharList, char *base32Char)
{
    
    const char *base32Chars = baseCharList;
    
    if (NULL == base32Chars) {
        base32Chars = defaultBaseList;
    }

    int componentSize = 5 - (size % 5);
    
    char tempBuf[size + componentSize + 1];
    
    if (componentSize != 0) {
        for (long i = 0; i < size; i++) {
            tempBuf[i] = buff[i];
        }
    }
    
    for (long i = 0; i < componentSize; i++) {
        tempBuf[i + size] = '\0';
    }
    
    tempBuf[size + componentSize] = '\0';
    
    long i = 0;
    char a;
    char b;
    char c;
    char d;
    char e;
    
    long j = 0;
    long base32Size = (size + componentSize) * 8 / 5 + 1;
    char base32Buff[base32Size];
    
    while (i < size + componentSize && j < base32Size) {
        a = tempBuf[i++];
        b = tempBuf[i++];
        c = tempBuf[i++];
        d = tempBuf[i++];
        e = tempBuf[i++];
        
        //          a          b             c          d            e
        //        11111111     11111111      11111111   11111111     11111111
        //        11111 111    11 11111 1    1111  1111 1   11111 11 111 11111
        //        1     2         3     4          5        6     7     8
        
        int index0 = (a & 0b11111000) >> 3;
        int index1 = ((a & 0b00000111) << 2) | ((b & 0b11000000) >> 6);
        int index2 = (b & 0b00111110) >> 1;
        int index3 = ((b & 0b00000001) << 4) | ((c & 0b11110000)  >> 4);
        int index4 = ((c & 0b00001111) << 1) | ((d & 0b10000000) >> 7);
        int index5 = ((d & 0b01111100) >> 2);
        int index6 = ((d & 0b00000011) << 3) | ((e & 0b11100000) >> 5);
        int index7 = (e & 0b00011111);
        
        base32Buff[j++] = base32Chars[index0];
        base32Buff[j++] = base32Chars[index1];
        base32Buff[j++] = base32Chars[index2];
        base32Buff[j++] = base32Chars[index3];
        base32Buff[j++] = base32Chars[index4];
        base32Buff[j++] = base32Chars[index5];
        base32Buff[j++] = base32Chars[index6];
        base32Buff[j++] = base32Chars[index7];
    }
    
    if (j < base32Size) {
        base32Buff[j] = '\0';
    }
    
    for (long i = 0; i < base32Size; i++) {
        base32Char[i] = base32Buff[i];
    }
    
    return base32Char;
}

static char *decodeBase32(const char *base32Char, const long base32CharSize, const char *baseCharList, char *originChar)
{
    
    if (NULL == base32Char || NULL == originChar) {
        return NULL;
    }
    
    const char *base32Chars = baseCharList;
    
    if (NULL == base32Chars) {
        base32Chars = defaultBaseList;
    }
    
    
    long originSize = strlen(base32Char) / 8 * 5 + 1;
    
    long buffSize = strlen(base32Char);
    
    long i = 0;
    long j = 0;
    
    while (i < buffSize & j < originSize) {
        char ch0 = base32Char[i++];
        char ch1 = base32Char[i++];
        char ch2 = base32Char[i++];
        char ch3 = base32Char[i++];
        char ch4 = base32Char[i++];
        char ch5 = base32Char[i++];
        char ch6 = base32Char[i++];
        char ch7 = base32Char[i++];
        
        int index0;
        indexChar(base32Chars, strlen(base32Chars), ch0, &index0);
        int index1;
        indexChar(base32Chars, strlen(base32Chars), ch1, &index1);
        int index2;
        indexChar(base32Chars, strlen(base32Chars), ch2, &index2);
        int index3;
        indexChar(base32Chars, strlen(base32Chars), ch3, &index3);
        int index4;
        indexChar(base32Chars, strlen(base32Chars), ch4, &index4);
        int index5;
        indexChar(base32Chars, strlen(base32Chars), ch5, &index5);
        int index6;
        indexChar(base32Chars, strlen(base32Chars), ch6, &index6);
        int index7;
        indexChar(base32Chars, strlen(base32Chars), ch7, &index7);
        
        //                       0           1                 2             3          4
        //        11111 111      11 11111 1      1111  1111     1  11111  11    111 11111
        //                  0     1       1   2   3       3     4       4   5     6     6      7
        
        char origin0 = ((index0 & 0b00011111) << 3)  | ((index1 & 0b00011100) >> 2) ;
        char origin1 = ((index1  & 0b00000011) << 6) | ((index2 & 0b00011111) << 1)  | ((index3 & 0b00010000) >> 4);
        char origin2 = ((index3  & 0b00001111) << 4) | ((index4 & 0b00011110) >> 1);
        char origin3 = ((index4 & 0b00000001) << 7) | ((index5 & 0b00011111) << 2) | ((index6 & 0b00011000) >> 3);
        char origin4 = ((index6 & 0b000000111) << 5) | (index7 & 0b00011111);
        
        originChar[j++] = origin0;
        originChar[j++] = origin1;
        originChar[j++] = origin2;
        originChar[j++] = origin3;
        originChar[j++] = origin4;
    }
    
    originChar[j] = '\0';
    
    return originChar;
}
    
static void indexChar(const char *buff, long buffSize, char ch, int *index)
{
    int i = 0;
    
    for (i = 0; i < buffSize; i++) {
        if (ch == buff[i]) {
            *index = i;
            break;
        }
    }
}
@end
